package netTracker.network;

import java.io.Serializable;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;

/** 
 * Stores the address of a location on the network.
 * Modified by David Cummings from the original source 
 * by Daniel Kaufman and Matthew Polk.
 */

@SuppressWarnings("serial")

public class Address implements Serializable, Comparable<Address>{
	/** The unique id of the address. */
	private int id;
	/** The port on which this address communicates. */
	private int port;
	/** The host name of this address. */
	private String hostname;
	/** Indicates whether this address is acting as a server. */
	private boolean isServer;
	
	/** 
	 * Constructs an Address.
	 * @param hostname the host name of this address
	 * @param port the port number of this address
	 * @param id the unique id of the process with this address
	 */
	public Address(String hostname, int port, int id){
		this.hostname = hostname;
		this.port = port;
		this.id = id;
		isServer = false;			// By default, an address is not a server
	}
	
	/** 
	 * Constructs an Address without an ID. Used in the initial 
	 * construction of an address, or for the address of a node 
	 * whose ID is inconsequential.
	 * 
	 * @param hostname the host name of this address
	 * @param port the port number of this address
	 */
	public Address(String hostname, int port){
		this.hostname = hostname;
		this.port = port;
	}
	
	/** 
	 * Constructs an Address without an ID or host name. 
	 * Used in the initial construction of an address, 
	 * or for the address of a node whose ID is inconsequential.
	 * 
	 * @param port the port number of this address
	 */
	public Address(int port){
		this.port = port;
	}
	
	/**
	 * Creates an empty address.
	 */
	public Address() {
		hostname = null;
		port = -1;
	}

	/**
	 * Compares another address' ID to this address' ID.
	 * @param addr the address to compare
	 * @return 	1  if this address' ID is greater than the compared address
	 * 			-1 if this address' ID is less than the compared address
	 * 			0  if this address' ID is the same as the compared address
	 */
	public int compareTo(Address addr) {
		int result = id - addr.id;
		if (result > 0)
			return 1;
		else if (result < 0) 
			return -1;
		return 0;
	}
	
	/**
	 * Compares an object to this address. Returns true if 
	 * both objects are addresses with identical information.
	 * @param o the object to compare
	 * @return true if the object is an address with identical information
	 */
	public boolean compareTo(Object o){
		try {
			Address other = (Address)o;
			return ((hostname == other.hostname) && (port == other.port) && hostname.equals(other.hostname));
		}catch (ClassCastException e){
			return false;	// If the object being compared is not an Address, return false.
		}
	}
	
	/** 
	 * Returns the ID of this address.
	 * @return the ID of this address
	 */
	public int getID(){
		return id;
	}
	
	/**
	 * Sets the id of this node.
	 * @param id The new id.
	 */
	public void setID(int id){
		this.id = id;
	}
	
	/**
	 * Returns the port number of this address.
	 * @return the port number of this address
	 */
	public int getPort(){
		return port;
	}
	
	/**
	 * Sets the port number of this node.
	 * @param port the new port number
	 */
	public void setPort(int port){
		this.port = port;
	}
	
	/**
	 * Returns the host name as a string.
	 * @return the hostname as a string
	 */
	public String getHostname(){
		return hostname;
	}
	
	/**
	 * Sets the host name of this node.
	 * @param hostname the new host name
	 */
	public void setHostname(String hostname){
		this.hostname = hostname;
	}
	
	/**
	 * Returns the InetAddress of this address.
	 * @return the InetAddress of this address.
	 * @throws UnknownHostException if the hostname is not a legitimate address. 
	 */
	public InetAddress getInet() throws UnknownHostException{
		return (InetAddress.getByName(hostname));
	}
	
	/**
	 * Returns the hostname and port as an InetSocketAddress.
	 * @return the hostname and port as an InetSocketAddress
	 */
	public InetSocketAddress getInetSocket(){
		return (InetSocketAddress.createUnresolved(hostname, port));
	}
	
	/**
	 * Sets if this address is acting as server.
	 * @param isServer whether or not this address should act as a server.
	 */
	public void setServer(boolean isServer){
		this.isServer = isServer;
	}
	
	/**
	 * Returns if this address is acting as server.
	 * @return if this address is acting as server.
	 */
	public boolean isServer(){
		return isServer;
	}
	
	/**
	 * Returns a string representation of this address.
	 * Example: 03@localhost:4998
	 * @return a string representation of this address.
	 */
	public String toString(){
		if (isServer)
			return ("SERVER" + id + "@" + hostname + ":" + port);
		else if(hostname==null)
			return "";
		else
			return (id + "@" + hostname + ":" + port);
	}

	/* (non-Javadoc)
	 * A hashCode method automatically generated by Eclipse.
	 * Note that it takes into account the hostname and the port, but not the ID.
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((hostname == null) ? 0 : hostname.hashCode());
		result = prime * result + port;
		return result;
	}

	/* (non-Javadoc)
	 * An equals method automatically generated by Eclipse.
	 * Note that it takes into account the hostname and the port, but not the ID.
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Address other = (Address) obj;
		if (hostname == null) {
			if (other.hostname != null)
				return false;
		} else if (!hostname.equals(other.hostname))
			return false;
		if (port != other.port)
			return false;
		return true;
	}
}
